#include <assert.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "hashtable.h"

int hashtable_str_hash (HashTable *tbl, const void *key)
{
	char *str = (char *)key;
	int hashcode = 0;
	int a = 33;
	int len = strlen (str);
	
	for (int i = 0; str[i] != 0; i++)
		hashcode += str[i] * (int)pow (a, len - (i + 1));
	
	return abs (hashcode) % tbl->size;
}

int hashtable_str_compare (HashTable *tbl, const void *key1, const void *key2)
{
	return strcmp ((char *)key1, (char *)key2);
}

void *hashtable_str_duplicate (HashTable *tbl, const void *key)
{
	return (void *)strdup ((char *)key);
}

int hashtable_resolve_collision_linear (HashTable *tbl, int hash, int i, int *state)
{
	return (hash + ++(*state)) % tbl->size;
}

HashTable *hashtable_new (int size, _fn_hash_function hash_function,
                                    _fn_compare_function compare_function,
                                    _fn_duplicate_function duplicate_function)
{
	HashTable *tbl;
	
	assert (tbl = malloc (sizeof (HashTable)));
	
	tbl->size = size;
	tbl->num_entries = 0;
	assert (tbl->entries = malloc (sizeof (HashTableEntry) * size));
	tbl->hash_function = hash_function;
	tbl->compare_function = compare_function;
	tbl->duplicate_function = duplicate_function;
	tbl->resolve_collision = hashtable_resolve_collision_linear;
	
	for (int i = 0; i < size; i++)
		tbl->entries[i].state = HTE_FREE;
	
	return tbl;
}

void hashtable_insert (HashTable *tbl, const void *key, void *value)
{
	int hash = tbl->hash_function (tbl, key);
	int i = hash;
	int state = 0;
	
	assert (tbl->num_entries < tbl->size);
	
	while (tbl->entries[i].state != HTE_FREE)
	{
		if (tbl->compare_function (tbl, key, tbl->entries[i].key) == 0)
			break;
		
		i = tbl->resolve_collision (tbl, hash, i, &state);
	}
	
	tbl->entries[i].state = HTE_OCCUPIED;
	tbl->entries[i].key = tbl->duplicate_function (tbl, key);
	tbl->entries[i].value = value;
}

void hashtable_remove (HashTable *tbl, const void *key)
{
	//TODO
}

void *hashtable_find (HashTable *tbl, const void *key)
{
	int hash = tbl->hash_function (tbl, key);
	int i = hash;
	int state = 0;
	
	while (tbl->entries[i].state != HTE_FREE)
	{
		if ((tbl->entries[i].key != NULL) && (tbl->compare_function (tbl, key, tbl->entries[i].key) == 0))
			return tbl->entries[i].value;
		
		i = tbl->resolve_collision (tbl, hash, i, &state);
	}
	
	return NULL;
}

